Android Tips #30 ViewPager を使ってスワイプで View を切り替える
はじめに
今回はスワイプで View を切り替えることができる ViewPager を使ってみたいと思います。 SupportPackage に含まれているので Android 1.6 から使うことができます! もう当たり前のように知っていて、よく使われているかたはかなり多いと思いますが、、知らないかたは、かなり便利なのでこの機会にぜひ覚えましょう!
まずは ViewPager を使ってみる
1. PageAdapter のカスタムクラスをつくる
まずは PageAdapter を実装したカスタムクラスをつくります。以下のメソッドが最低限必要です。
isViewFromObject() | Object に View が含まれているか判定する。 |
---|---|
getCount() | ViewPager に登録する全アイテム数を返す。 |
instantiateItem() | アイテムを追加するときに呼ばれる。このメソッド内で View をコンテナに追加する。 |
destroyItem() | アイテムを削除するときに呼ばれる。このメソッド内で View の削除をおこなう。 |
PageAdapter では基本的にアイテムは Object として取り扱います。instantiateItem() で返すオブジェクトは Object ですし、 destroyItem() で渡される値も Object です。実際は ViewGroup か View が渡される感じになると思います。 isViewFromObject() は頻繁に呼ばれるメソッドで、 Object の中に View が含まれているか判定するためのメソッドです。Object が ViewGroup の場合は 対象の View まで掘り下げて判定してあげる必要があります。Object が 単純な View であれば Object#equals() の判定で良いと思います。 getCount() は ViewPager で表示したいアイテム数を返します。アイテムの情報を Object として持ちたい場合は ArrayList をつくって size() を渡すようにします。 以下の例では色のリストをセットでき、アイテムの色がテキスト色になる TextView を表示する PagerAdapter をつくってみました。 add() メソッドをつくり、外からアイテムを設定できるようにしました。
package jp.classmethod.android.sample.viewpager; import java.util.ArrayList; import android.content.Context; import android.support.v4.view.PagerAdapter; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; /** * カスタム PagerAdapter クラス. */ public class CustomPagerAdapter extends PagerAdapter { /** コンテキスト. */ private Context mContext; /** リスト. */ private ArrayList<Integer> mList; /** * コンストラクタ. */ public CustomPagerAdapter(Context context) { mContext = context; mList = new ArrayList<Integer>(); } /** * リストにアイテムを追加する. * @param item アイテム */ public void add(Integer item) { mList.add(item); } @Override public Object instantiateItem(ViewGroup container, int position) { // リストから取得 Integer item = mList.get(position); // View を生成 TextView textView = new TextView(mContext); textView.setText("Page:" + position); textView.setTextSize(30); textView.setTextColor(item); textView.setGravity(Gravity.CENTER); // コンテナに追加 container.addView(textView); return textView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { // コンテナから View を削除 container.removeView((View) object); } @Override public int getCount() { // リストのアイテム数を返す return mList.size(); } @Override public boolean isViewFromObject(View view, Object object) { // Object 内に View が存在するか判定する return view == (TextView) object; } }
2. ViewPager に Adapter をセットする
あとは ViewPager をレイアウトに追加し setAdapter() で先ほどつくったカスタム PagerAdapter をインスタンス化しセットするだけです。先ほどのクラスでは add() メソッドを用意しておいたので、それを使ってアイテムを追加します。
package jp.classmethod.android.sample.viewpager; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.support.v4.view.ViewPager; /** * ViewPager を表示するサンプル. */ public class PagerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // カスタム PagerAdapter を生成 CustomPagerAdapter adapter = new CustomPagerAdapter(this); adapter.add(Color.BLACK); adapter.add(Color.RED); adapter.add(Color.GREEN); adapter.add(Color.BLUE); adapter.add(Color.CYAN); adapter.add(Color.MAGENTA); adapter.add(Color.YELLOW); // ViewPager を生成 ViewPager viewPager = new ViewPager(this); viewPager.setAdapter(adapter); // レイアウトにセット setContentView(viewPager); } }
これで完成です!スワイプするとページが切り替わります!
応用編1 : ボタンでページを切り替える・ページを動的に追加する
応用編ということで、戻る/進むボタンを置いてページを切り替えるようにして、追加ボタンでページを追加できるようにしてみました! ページの切り替えは ViewPager#setCurrentItem() でおこないます。ViewPager#getCurrentItem() で現在表示されているアイテムの Index を取得し、それを足したり引いたりしています。リストの範囲外になっても Exception などは起きないので、よくある Index の調整は不要です。 ページを動的に追加するのは先ほどのカスタム PagerAdapter で実装した add() メソッドを使うと簡単におこなえます。
PagerControlActivity.java
package jp.classmethod.android.sample.viewpager; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.view.View; import android.view.View.OnClickListener; /** * ViewPager をコントロールする機能を追加するサンプル. */ public class PagerControlActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pager_control); // カスタム PagerAdapter を生成 CustomPagerAdapter adapter = new CustomPagerAdapter(this); adapter.add(Color.BLACK); adapter.add(Color.RED); adapter.add(Color.GREEN); adapter.add(Color.BLUE); adapter.add(Color.CYAN); adapter.add(Color.MAGENTA); adapter.add(Color.YELLOW); // ViewPager を生成 ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager); viewPager.setAdapter(adapter); // 戻るボタン findViewById(R.id.preview_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager); viewPager.setCurrentItem(viewPager.getCurrentItem() - 1); } }); // 進むボタン findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager); viewPager.setCurrentItem(viewPager.getCurrentItem() + 1); } }); // 追加ボタン findViewById(R.id.add_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager); CustomPagerAdapter adapter = (CustomPagerAdapter) viewPager.getAdapter(); // ランダムに色を追加する int R = (int)( Math.random() * 256); int G = (int)( Math.random() * 256); int B = (int)( Math.random() * 256); adapter.add(Color.rgb(R, G, B)); adapter.notifyDataSetChanged(); } }); } }
activity_pager_control.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" > <Button android:id="@+id/preview_button" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="PREVIEW" /> <Button android:id="@+id/add_button" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="ADD" /> <Button android:id="@+id/next_button" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="NEXT" /> </LinearLayout> </RelativeLayout>
応用編2 : 画像ギャラリーをつくってみる
最後に、さらなる応用編として端末内の画像を読み込んで表示するギャラリーをつくってみました。基本は上記までの方法と一緒です。 Cursor の読み込みはこれまた SupportPackage にある CursorLoader を使って非同期で読み込んでみました。AsyncTaskLoader の具体的な解説はこちらに載っているので参考にしてください。
ImagePagerAdapter.java
package jp.classmethod.android.sample.viewpager; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import android.content.ContentResolver; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.provider.MediaStore; import android.support.v4.view.PagerAdapter; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; /** * 画像を表示する PagerAdapter. */ public class ImagePagerAdapter extends PagerAdapter { /** コンテキスト. */ private Context mContext; /** ContentResolver. */ private ContentResolver mResolver; /** ID のリスト. */ private ArrayList<Long> mList; /** * コンストラクタ. * @param context {@link Context} */ public ImagePagerAdapter(Context context) { mContext = context; mResolver = mContext.getContentResolver(); mList = new ArrayList<Long>(); } /** * アイテムを追加する. * @param id ID */ public void add(Long id) { mList.add(id); } @Override public Object instantiateItem(ViewGroup container, int position) { // リストから取得 Long id = mList.get(position); Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id.toString()); Bitmap bitmap = null; try { bitmap = getBitmap(uri); } catch (IOException e) { e.printStackTrace(); } // View を生成 ImageView imageView = new ImageView(mContext); imageView.setImageBitmap(bitmap); // コンテナに追加 container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { // コンテナから View を削除 container.removeView((View) object); } @Override public int getCount() { return mList.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view.equals(object); } /** * Bitmap を取得する. * @param imageUri 画像の URI * @return Bitmap * @throws IOException */ public Bitmap getBitmap(Uri imageUri) throws IOException { BitmapFactory.Options mOptions = new BitmapFactory.Options(); mOptions.inSampleSize = 10; Bitmap resizeBitmap = null; InputStream is = mResolver.openInputStream(imageUri); resizeBitmap = BitmapFactory.decodeStream(is, null, mOptions); is.close(); return resizeBitmap; } }
ImagePagerActivity.java
package jp.classmethod.android.sample.viewpager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; /** * 画像を使った ViewPager のサンプル. */ public class ImagePagerActivity extends FragmentActivity { /** ViewPager. */ private ViewPager mPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ViewPager をレイアウトにセット mPager = new ViewPager(this); setContentView(mPager); // CursorLoader を呼び出す getSupportLoaderManager().initLoader(0, null, callbacks); } /** CursorLoader のコールバック. */ private LoaderCallbacks<Cursor> callbacks = new LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle bundle) { Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; return new CursorLoader(getApplicationContext(), uri, null, null, null, null); } @Override public void onLoaderReset(Loader<Cursor> loader) { } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor c) { // Cursor から id を取得して PagerAdapter に入れる ImagePagerAdapter adapter = new ImagePagerAdapter(ImagePagerActivity.this); c.moveToFirst(); do { long id = c.getLong(c.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)); adapter.add(id); } while (c.moveToNext()); // ViewPager にセット mPager.setAdapter(adapter); } }; }
ソースコード
ソースコードを github に公開しました!ぜひ実装時の参考にしてください!
まとめ
今回は ViewPager の基本と、少しだけですが応用してみました。画像ギャラリーは本来であればもう少し調整すべき点は(いくつも)ありますが、基本的な実装はこれだけで済むので非常に楽ですね!用途はさまざまありますし Android 1.6 から使えるのでかなり有用です。まずは使ってみてください!